package hn.sigit.logic.viewer.toolbox;

import hn.sigit.dao.hnd.cadastre.HND_ParcelDAO;
import hn.sigit.dao.hnd.ladmshadow.ParcelDAO;
import hn.sigit.dao.hnd.ladmshadow.PropertyDAO;
import hn.sigit.logic.geometry.GeometryOperations;
import hn.sigit.logic.geometry.SplitException;
import hn.sigit.logic.geometry.SplitPolygonResult;
import hn.sigit.logic.viewer.InteractiveViewerHelper;
import hn.sigit.model.commons.IParcel;
import hn.sigit.model.commons.IProperty;
import hn.sigit.model.hnd.cadastre.HND_Parcel;
import hn.sigit.model.hnd.ladmshadow.Parcel;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

import org.hibernatespatial.mgeom.MCoordinate;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;


public class SplitViewerCallable implements ViewerCallable, Serializable {
	private static final long serialVersionUID = 1L;

	@Override
	public void afterMerge(IParcel parcel1, IProperty property1,
			IParcel parcel2, IProperty property2, Polygon newShape,
			Date timeStamp) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void doBorderOperation(
			InteractiveViewerHelper interactiveViewerHelper) {
		BorderHelper bh = interactiveViewerHelper.getBorderHelper();
		
		//Creates the split border as a linestring object from the input coordinates
		Date timeStamp = new Date();
		
		int borderSize = bh.getCurrentBorder().size();
		
		if (borderSize < 2) {
			FacesContext.getCurrentInstance().addMessage("",
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							interactiveViewerHelper.getResBundle().loadMessage("dataentry.split.no_split_possible") + ": ",
							interactiveViewerHelper.getResBundle().loadMessage("dataentry.split.no_boundary")));
		}
		
		BorderHelper.Coord c;
		Coordinate[] coordinates = new MCoordinate[borderSize];
		for (int i = 0; i < coordinates.length; i++) {
			c = bh.getCurrentBorder().get(i);
			coordinates[i] = new MCoordinate(c.getX(), c.getY());
		}
		
		IParcel parcelToSplit = interactiveViewerHelper.getSelectedParcel(); //ParcelDAO.loadParcelById(parcelToSplitId);
		
		if (parcelToSplit == null) {
			FacesContext.getCurrentInstance().addMessage("",
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							interactiveViewerHelper.getResBundle().loadMessage("dataentry.split.no_split_possible") + ": ",
							interactiveViewerHelper.getResBundle().loadMessage("dataentry.split.no_parcel_to_split")));
		}

		//proceed to split the parcel geometry object
		Polygon polygonToSplit = parcelToSplit.getShape();
		try {
			SplitPolygonResult spr = GeometryOperations.splitPolygon(polygonToSplit, coordinates);

			//TODO: Version 2: Cambiar a HashMap mapeando de llave a parcela
			List<? extends IParcel> effectiveParcelNeighbors;
			if (parcelToSplit instanceof Parcel)
				effectiveParcelNeighbors = ParcelDAO.loadNeighborsByParcel((Parcel) parcelToSplit);
			else
				effectiveParcelNeighbors = HND_ParcelDAO.loadNeighborsByHNDParcel((HND_Parcel) parcelToSplit);
			Map<Long,IParcel> correctedNeighbors = GeometryOperations.eliminateGaps(parcelToSplit, effectiveParcelNeighbors, spr);
			
			/*
			 * NOTE: This commented code is if we want to create a new parcel object version
			 * if we just changed the geometry of this neighbor parcel
			 * 
			for (Parcel oldParcel : effectiveParcelNeighbors) {
				Parcel newParcel = correctedNeighbors.get(oldParcel.getSuID());
				if (newParcel != null) {
					Util.replaceParcel(oldParcel, newParcel, timeStamp, false);
				}
			}
			*/

			/*NOTE: This code only modifies the geometry object without creating
			 * a new version of it
			 */
			for (IParcel neighborParcel : effectiveParcelNeighbors) {
				IParcel correctedParcel = correctedNeighbors.get(neighborParcel.getSuID());
				//since not all neighbors have to be corrected, we have to check for null here
				if (correctedParcel != null) {
					neighborParcel.setShape(correctedParcel.getShape());
					if (neighborParcel instanceof Parcel) {
						((Parcel) neighborParcel).setModified(true);
						((Parcel) neighborParcel).setOriginal(false);
						ParcelDAO.save(neighborParcel);
					}
					else if (neighborParcel instanceof HND_Parcel) {
						HND_ParcelDAO.save(neighborParcel);
					}
				}
			}
			
			
			IProperty originalProperty = parcelToSplit.getProperty();
			
			
			IProperty newProperty1 = originalProperty.clone();
			newProperty1.setBeginLifespanVersion(timeStamp);
			
			IParcel newParcel1 = parcelToSplit.clone();
			setNewParcelPropValues(interactiveViewerHelper, newParcel1,
					timeStamp, spr.getSplitPolygon1(), newProperty1);
			
			
			newProperty1.getSpatialUnits().clear();
			newProperty1.getSpatialUnits().add(newParcel1);
			
			ParcelDAO.save(newParcel1);
			PropertyDAO.save(newProperty1);

			
			
			IProperty newProperty2 = originalProperty.clone();
			newProperty2.setBeginLifespanVersion(timeStamp);
			
			IParcel newParcel2 = parcelToSplit.clone();
			setNewParcelPropValues(interactiveViewerHelper, newParcel2,
					timeStamp, spr.getSplitPolygon2(), newProperty2);
			
			newProperty2.getSpatialUnits().clear();
			newProperty2.getSpatialUnits().add(newParcel2);

			ParcelDAO.save(newParcel2);
			PropertyDAO.save(newProperty2);

			//mark as modified
			if (parcelToSplit instanceof Parcel)
				((Parcel) parcelToSplit).setModified(true);
			parcelToSplit.setEndLifespanVersion(timeStamp);
			originalProperty.setEndLifespanVersion(timeStamp);
			
			 
			ParcelDAO.save(parcelToSplit);
			PropertyDAO.save(originalProperty);

			bh.clearBoundary();
			
			interactiveViewerHelper.setSelectedZone(newParcel1);
		}
		catch (SplitException se) {
			FacesContext.getCurrentInstance().addMessage("",
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							interactiveViewerHelper.getResBundle().loadMessage(se.getExceptionBundle()) + ": ",
							interactiveViewerHelper.getResBundle().loadMessage(se.getMessage())));
			se.printStackTrace();
		}
	}
	
	private void setNewParcelPropValues(InteractiveViewerHelper interactiveViewerHelper,
			IParcel theNewParcel, Date timeStamp, Polygon shape, IProperty property) {
		theNewParcel.setBeginLifespanVersion(timeStamp);
		theNewParcel.setShape(shape);
		theNewParcel.getBaunits().clear();
		theNewParcel.getBaunits().add(property);

		//assigns new fieldtab number
		theNewParcel.setFieldTab(interactiveViewerHelper.getFieldTabCounter());
		
		//Let's better not inherit easements and improvements
		theNewParcel.getEasements().clear();
		theNewParcel.getImprovements().clear();

		//new parcel's municipal and cadastral keys must be null to avoid repeated keys!
		theNewParcel.setMunicipalKey(null);
		theNewParcel.setCadastralKey(null);
		
		//HND_Parcel specific attributes are not inherited....
		if (theNewParcel instanceof HND_Parcel) {
			((HND_Parcel) theNewParcel).setOriginatingTransaction(null);
			((HND_Parcel) theNewParcel).setSpatialZoneInTransactions(null);
			((HND_Parcel) theNewParcel).setPermits(null);
			((HND_Parcel) theNewParcel).setSpatialZoneAttrValues(null);
		}
	}
}
